﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Weka
{
    internal class StreamReada
    {
        private readonly Stream _stream;
        private byte[] _buffer = new byte[16];
        private int _headerBitsToRead;
        private byte _headerByte;
        //        public byte MessageId { get; private set; }
        public StreamReada(Stream stream)
        {
            _stream = stream;
        }

        /*
         public MemoryStream ReadRawStructureData()
                {
                    MessageId = ReadByte();
            
                    MemoryStream ms = new MemoryStream(32);
                    ReadRawStructureDataOfMessage(ms);
                    ms.Position = 0;
                    return ms;
                }
                private void ReadRawStructureDataOfMessage(MemoryStream ms)
                {
                    byte fieldCount = ReadByte();
                    ms.WriteByte(fieldCount);
                    for (int i = 0; i < fieldCount; i++)
                    {
                        var fi = new FieldInfo(_stream);
                        fi.Write(ms);
                        if (fi.FieldType != FieldType.PrimaryData)
                            ReadRawStructureDataOfMessage(ms);
                    }
                }
                */

        public FieldInfo ReadFieldInfo()
        {
            return new FieldInfo(_stream);
        }
        internal FieldInfo ReadFieldInfoAndSema()
        {
            var fi = ReadFieldInfo();
            if (fi.FieldType == FieldType.PrimaryData) return fi;

            byte count = ReadByte();
            for (int i = 0; i < count; i++)
                ReadFieldInfoAndSema();

            return fi;
        }


        public byte ReadByte() { return (byte)_stream.ReadByte(); }


        public void Write(byte v)
        {
            _stream.WriteByte(v);
        }

        public Int16 ReadInt16()
        {
            return unchecked((Int16)ReadUInt16());
        }
        public UInt16 ReadUInt16()
        {
            int f = _stream.ReadByte();
            return (UInt16)(_stream.ReadByte() | (f << 8));
        }

        public Int32 ReadInt32()
        {
            return unchecked((Int32)ReadUInt32());
        }
        public UInt32 ReadUInt32()
        {
            unchecked
            {
                _stream.Read(_buffer, 0, 4);
                return (UInt32)((_buffer[0] << 24) | (_buffer[1] << 16) | (_buffer[2] << 8) | (_buffer[3]));
            }
        }
        public Int64 ReadInt64()
        {
            return unchecked((Int64)ReadUInt64());
        }
        public UInt64 ReadUInt64()
        {
            //            Buffer.BlockCopy(_buffer, 0, _bufferInt64, 0, 8);

            UInt32 v1 = ReadUInt32();
            UInt32 v2 = ReadUInt32();

            return (((UInt64)v1) << 32) | v2;
        }


        internal string ReadStringRaw(int byteCount)
        {
            if (_buffer.Length < byteCount) _buffer = new byte[byteCount /*+ (l >> 1)*/];
            _stream.ReadFull(_buffer, byteCount);
            return UTF8Encoding.UTF8.GetString(_buffer, 0, byteCount);
        }

        public void HeaderClear()
        {
            _headerBitsToRead = 0;
        }

        public byte HeaderReadBit()
        {
            if (_headerBitsToRead == 0)
            {
                _headerByte = (byte)_stream.ReadByte();
                _headerBitsToRead = 8;
            }
            return (byte)((_headerByte >> (8 - _headerBitsToRead--)) & 0x01);
        }

        public byte HeaderReadBits2()
        {
            if (_headerBitsToRead > 1)
            {
                _headerBitsToRead -= 2;
                return (byte)((_headerByte >> (6 - _headerBitsToRead)) & 0x03);
            }

            if (_headerBitsToRead == 1)
            {
                int f = ((_headerByte >> 7) & 0x01);

                _headerByte = (byte)_stream.ReadByte();
                _headerBitsToRead = 7;
                return (byte)(((_headerByte & 0x01) << 1) | f);
            }

            _headerByte = (byte)_stream.ReadByte();
            _headerBitsToRead = 6;
            return (byte)(_headerByte & 0x03);
        }


        public byte HeaderReadBits4()
        {
            if (_headerBitsToRead > 3)
            {
                _headerBitsToRead -= 4;
                return (byte)((_headerByte >> (8 - _headerBitsToRead)) & 0x0F);
            }

            int v = _headerByte >> (8 - _headerBitsToRead);

            int bitsStillToRead = 4 - _headerBitsToRead;

            _headerByte = (byte)_stream.ReadByte();

            _headerBitsToRead = 8 - bitsStillToRead;

            v |= (_headerByte << (4 - bitsStillToRead));

            v &= 0x0F;

            return (byte)v;
        }



        private void HeaderReset()
        {
            _headerBitsToRead = 0;
        }


        internal void Populate(byte[] data)
        {
            _stream.ReadFull(data, data.Length);
        }
    }
}
